import Database from 'better-sqlite3';
import {DbMetadataUtils, TransactionUtils} from '../common';
import {setEntityMetadata} from "../decorators";
import {EntityClass} from "../types";

/**
 * 数据库初始化器
 * 负责创建表和初始化数据库结构
 */
export class DbInitializer {
  private db: Database.Database;
  private entityClasses: EntityClass[];

  /**
   * 创建新的数据库初始化器
   * @param db 数据库实例
   * @param entityClasses 要初始化的实体类列表
   */
  constructor(db: Database.Database, entityClasses: EntityClass[] = []) {
    this.db = db;
    this.entityClasses = entityClasses;
  }

  /**
   * 注册一个实体类用于初始化
   * @param entityClass 要注册的实体类
   */
  registerEntity(entityClass: EntityClass): void {
    if (!this.entityClasses.includes(entityClass)) {
      this.entityClasses.push(entityClass);
    }
  }

  /**
   * 初始化所有注册的实体表
   * @param force 是否强制重新创建表（即使已存在）
   * @returns 创建的表数量
   */
  initTables(force: boolean = false): number {
    if (this.entityClasses.length === 0) {
      return 0;
    }

    let tablesCreated = 0;

    TransactionUtils.executeTransaction(this.db, () => {
      // 优先创建没有外键依赖的表
      const sortedEntities = this.sortEntitiesByDependency();

      for (const entityClass of sortedEntities) {
        const tableInfo = entityClass.getTableInfo();

        // 添加列信息到实体元数据
        setEntityMetadata(entityClass, {
          tableName: tableInfo.name,
          primaryKey: tableInfo.primaryKey,
          columns: tableInfo.columns || {},
          indices: tableInfo.indices,
          db: tableInfo.db
        })

        const tableName = tableInfo.name;

        const tableExists = DbMetadataUtils.isTableExists(this.db, tableName);

        if (!tableExists || force) {
          if (tableExists && force) {
            // 如果表存在且强制重建，先删除
            this.db.exec(`DROP TABLE IF EXISTS ${tableName}`);
          }

          // 创建表
          this.db.exec(entityClass.getCreateTableSQL());
          tablesCreated++;
        }
      }
    });

    return tablesCreated;
  }

  /**
   * 根据外键依赖关系排序实体
   * 没有依赖的实体会排在前面
   * @returns 排序后的实体数组
   */
  private sortEntitiesByDependency(): EntityClass[] {
    const entityMap = new Map<string, { entity: EntityClass; dependencies: string[] }>();

    // 收集所有实体和它们的依赖
    for (const entityClass of this.entityClasses) {
      const tableInfo = entityClass.getTableInfo<any>();
      const dependencies: string[] = [];

      // 收集外键引用的表
      for (let columnName in tableInfo.columns) {
        const column = tableInfo.columns[columnName];
        if (column.foreignKey) {
          dependencies.push(column.foreignKey.table);
        }
      }

      entityMap.set(tableInfo.name, {entity: entityClass, dependencies});
    }

    // 拓扑排序
    const result: EntityClass[] = [];
    const visited = new Set<string>();
    const visiting = new Set<string>();

    // 深度优先搜索访问所有节点
    function visit(tableName: string): void {
      if (visited.has(tableName)) return;
      if (visiting.has(tableName)) {
        throw new Error(`检测到循环依赖，包含表: ${tableName}`);
      }

      visiting.add(tableName);

      const info = entityMap.get(tableName);
      if (info) {
        // 先访问所有依赖
        for (const dep of info.dependencies) {
          // 只处理我们知道的实体
          if (entityMap.has(dep)) {
            visit(dep);
          }
        }

        result.push(info.entity);
        visited.add(tableName);
      }

      visiting.delete(tableName);
    }

    // 访问所有未访问的节点
    for (const tableName of entityMap.keys()) {
      if (!visited.has(tableName)) {
        visit(tableName);
      }
    }

    // 反转结果，因为我们需要先创建没有依赖的表
    return result.reverse();
  }

  /**
   * 检查并初始化缺少的表
   * 只创建尚不存在的表
   * @returns 创建的表数量
   */
  checkAndInitMissingTables(): number {
    let tablesCreated = 0;

    TransactionUtils.executeTransaction(this.db, () => {
      for (const entityClass of this.entityClasses) {
        const tableInfo = entityClass.getTableInfo();
        const tableName = tableInfo.name;

        if (!DbMetadataUtils.isTableExists(this.db, tableName)) {
          // 创建表
          this.db.exec(entityClass.getCreateTableSQL());
          tablesCreated++;
        }
      }
    });

    return tablesCreated;
  }
}
